package dao

import (
	"sftti-go-service/pkg/db"
	"sftti-go-service/types"
	"strconv"
	"strings"

	"github.com/dubbogo/gost/log/logger"
)

type PaperDaoImpl struct {
}

func (*PaperDaoImpl) QueryPaper(params map[string]any) (res types.Paper, err error) {

	sql := `
		select
        p.id,
        p.status,
        p.createdAt,
        p.updatedAt,
        p.title,
        p.roleType,
        p.description,
        p.answerChoices,
        p.code,
		(select count(1) from paper_user_done where userId=#{userId} and paperId=p.id) as hasDone,
		p.price
        from paper p
        where p.status=#{paperId}
        and p.id=1
		`

	for key, value := range params {

		switch t := value.(type) {
		case int64:
			sql = strings.ReplaceAll(sql, "#{"+key+"}", strconv.FormatInt(t, 10))
		case string:
			sql = strings.ReplaceAll(sql, "#{"+key+"}", string(t))
		}

	}
	err = db.DB.Get(&res,
		sql,
	)

	if err != nil {
		logger.Errorf("QueryPaper_Err%s", err)
	}

	return res, err
}

func (*PaperDaoImpl) QueryPaperQuestionChoices(questionId int64) (res []types.PaperQuestionChoice, err error) {
	sql := `
		select
        pqc.id,
        pqc.createdAt,
        pqc.updatedAt,
        pqc.status,
        pqc.content,
        pqc.type,
        pqc.questionId,
        pqc.order
        from paper_question_choice pqc
        where pqc.questionId=?
        order by pqc.order asc
	`
	err = db.DB.Select(&res,
		sql,
		questionId,
	)

	if err != nil {
		logger.Errorf("QueryPaperQuestionChoices_Err%s", err)
	}

	return res, err
}

func (s *PaperDaoImpl) QueryPaperQuestions(paperId int64) (res []types.PaperQuestion, err error) {
	sql := `
		select
        pq.id,
        pq.createdAt,
        pq.updatedAt,
        pq.status,
        pq.content,
        pq.order,
        pq.paperId,
        pq.type
        from paper_question as pq
        where pq.status=1
       	and pq.paperId=?
        order by pq.order asc
	`

	err = db.DB.Select(&res,
		sql,
		paperId,
	)

	for i := 0; i < len(res); i++ {
		var choices []types.PaperQuestionChoice
		choices, err = s.QueryPaperQuestionChoices(res[i].Id)
		res[i].Choices = choices
	}

	if err != nil {
		logger.Errorf("QueryPaperQuestions_Err%s", err)
	}

	return res, err
}

func (s *PaperDaoImpl) CheckUserPaperAuth(paperId int64, userId int64) (res bool, err error) {
	sql := `
		select
        (
            (
                select count(1)
                from paper p where p.id=#{paperId} and p.roleType=100
            )
            +
            (
                select count(1)
                from class_paper_rel cpr
                join class_user_rel cur
                on(cpr.classId=cur.classId and cur.userId=#{userId})
                where cpr.paperId=#{paperId}
                and (select count(1) from paper p where p.id=#{paperId} and p.roleType in (select ur.roleType from user_role ur where ur.userId = #{userId}))>0
            )
            +
            (
                select count(1)
                from class c
                join class_paper_rel cpr
                on(c.id=cpr.classId and cpr.paperId=#{paperId})
                where c.userId=#{userId}
                and (select count(1) from paper p where p.id=#{paperId} and p.roleType in (select ur.roleType from user_role ur where ur.userId = #{userId}))>0
            )
        ) as auth
	`

	sql = strings.ReplaceAll(sql, "#{userId}", strconv.FormatInt(userId, 10))
	sql = strings.ReplaceAll(sql, "#{paperId}", strconv.FormatInt(paperId, 10))

	err = db.DB.Get(&res,
		sql,
	)

	if err != nil {
		logger.Errorf("CheckUserPaperAuth_Err%s", err)
	}

	return res, nil
}

func (s *PaperDaoImpl) CheckUserHasDonePaper(paperId int64, userId int64) (res bool, err error) {
	sql := `
		select count(1) from paper_user_done where paperId=? and userId=?
	`

	err = db.DB.Get(&res,
		sql,
		paperId,
		userId,
	)

	if err != nil {
		logger.Errorf("CheckUserPaperAuth_Err%s", err)
	}

	return res, nil
}

func (s *PaperDaoImpl) AddPaperUserAnswer(answer *types.PaperUserAnswer) (res int64, err error) {
	sql := `
		insert into paper_user_answer(createdAt,updatedAt,status,questionId,userId,answer)
        values(?,?,?,?,?,?)
	`

	r, err := db.DB.Exec(
		sql,
		answer.CreatedAt,
		answer.UpdatedAt,
		answer.Status,
		answer.QuestionId,
		answer.UserId,
		answer.Answer,
	)

	if err != nil {
		logger.Errorf("AddPaperUserAnswer_Err%s", err)
		return res, err
	}

	id, err := r.LastInsertId()
	if err != nil {
		logger.Errorf("AddPaperUserAnswer_ID_Err%s", err)
		return res, err
	}

	return id, nil
}

func (s *PaperDaoImpl) AddPaperUserDone(pud *types.PaperUserDone) (res int64, err error) {
	sql := `
		insert into paper_user_done(createdAt,updatedAt,status,paperId,userId)
        values(?,?,?,?,?)
	`

	r, err := db.DB.Exec(
		sql,
		pud.CreatedAt,
		pud.UpdatedAt,
		pud.Status,
		pud.PaperId,
		pud.UserId,
	)

	if err != nil {
		logger.Errorf("AddPaperUserDone_Err%s", err)
		return res, err
	}

	id, err := r.LastInsertId()
	if err != nil {
		logger.Errorf("AddPaperUserDone_ID_Err%s", err)
		return res, err
	}

	return id, nil
}

func (s *PaperDaoImpl) CheckQuestionInPaper(paperId int64, questionId int64) (res bool, err error) {

	sql := `
		select count(1) from paper_question where id=? and paperId=?
	`

	err = db.DB.Get(&res,
		sql,
		questionId,
		paperId,
	)

	if err != nil {
		logger.Errorf("CheckQuestionInPaper_Err%s", err)
	}

	return res, nil
}

func (s *PaperDaoImpl) QueryPaperUserAnswers(paperId int64, userId int64) (res []types.PaperUserAnswer, err error) {

	sql := `
		select
        pua.id,
        pua.createdAt,
        pua.updatedAt,
        pua.status,
        pua.questionId,
        pua.answer,
        pua.userId
        from paper_question pq
        join paper_user_answer pua
        on(pq.id=pua.questionId and pq.paperId=?)
        where pua.status=1 and pua.userId=?
	`

	err = db.DB.Select(&res,
		sql,
		paperId,
		userId,
	)

	if err != nil {
		logger.Errorf("QueryPaperUserAnswers_Err%s", err)
	}

	return res, err
}

func (s *PaperDaoImpl) QueryUserPapers(params map[string]any) (res []types.Paper, err error) {
	sql := `
		select
        p.id,
        p.status,
        p.createdAt,
        p.updatedAt,
        p.title,
        p.roleType,
        p.price,
        p.code,
        (select count(1) from paper_user_done where userId=#{userId} and paperId=p.id) as hasDone
        from paper p
        where p.status=1
        and (
            p.roleType=100 or
            p.roleType in (select ur.roleType from user_role ur where ur.userId = #{userId})
        )
        and
        (
            (
                select count(1)
                from class_paper_rel cpr
                join class_user_rel cur
                on(cpr.classId=cur.classId and cur.userId=#{userId})
                where cpr.paperId=p.id
            ) > 0
            or
            (
                select count(1)
                from class c
                join class_paper_rel cpr
                on(c.id=cpr.classId and p.id=cpr.paperId)
                where c.userId=#{userId}
            ) > 0
        )
		` +
		(func() string {
			if params["prevId"] == nil {
				return ""
			} else {
				return "and p.id<#{prevId}"
			}
		})() +
		`
		order by p.id desc
		limit #{pageSize}
		`

	for key, value := range params {
		switch t := value.(type) {
		case int64:
			sql = strings.ReplaceAll(sql, "#{"+key+"}", strconv.FormatInt(t, 10))
		case string:
			sql = strings.ReplaceAll(sql, "#{"+key+"}", string(t))
		}
	}
	err = db.DB.Select(&res,
		sql,
	)

	if err != nil {
		logger.Errorf("QueryUserPapers_Err%s", err)
	}

	return res, err
}

func (s *PaperDaoImpl) QueryUserDonePapers(params map[string]any) (res []types.Paper, err error) {
	sql := `
		select
        p.id,
        p.status,
        p.createdAt,
        p.updatedAt,
        p.title,
        p.roleType,
        1 as hasDone,
        p.price,
        p.code,
        p.description,
        pud.createdAt as doneTime
        from paper p
        join paper_user_done pud
        on(p.id=pud.paperId)
        where pud.userId=#{userId}
		` +
		(func() string {
			if params["prevId"] == nil {
				return ""
			} else {
				return "and p.id<#{prevId}"
			}
		})() +
		`
		order by p.id desc
		limit #{pageSize}
		`

	for key, value := range params {
		switch t := value.(type) {
		case int64:
			sql = strings.ReplaceAll(sql, "#{"+key+"}", strconv.FormatInt(t, 10))
		case string:
			sql = strings.ReplaceAll(sql, "#{"+key+"}", string(t))
		}
	}
	err = db.DB.Select(&res,
		sql,
	)

	if err != nil {
		logger.Errorf("QueryUserDonePapers_Err%s", err)
	}

	return res, err
}
